package com.labofjet.system.service.impl;

import com.labofjet.common.dto.ContextDto;
import com.labofjet.common.service.RedisService;
import com.labofjet.common.util.JSONUtils;
import com.labofjet.system.dto.SysLoginDto;
import com.labofjet.system.dto.SysUserDto;
import com.labofjet.system.dto.SysUserExample;
import com.labofjet.system.enums.ERedisDefine;
import com.labofjet.system.enums.EState;
import com.labofjet.system.mapper.SysUserMapper;
import org.apache.http.client.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.labofjet.system.enums.ERedisDefine.USER_LOGIN_FAIL_TIMES;


@Service
public class SysLoginServiceImpl extends CrudBaseServiceImpl<SysLoginDto> {

    @Autowired
    private RedisService redisService;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private SysUserServiceImpl sysUserService;

    public SysLoginDto login(ContextDto contextDto) {
        SysLoginDto sysLoginDto = JSONUtils.getRequestData(contextDto, SysLoginDto.class);
        SysUserDto user = this.findUserByUsernameAndPassword(sysLoginDto.getUsername(), sysLoginDto.getPassword());
        if (user == null) {
            contextDto.setSuccess(false);
            contextDto.setMessage("账户名或者密码错误");
            return null;
        }
        if (!EState.VALID.getCode().equals(user.getState())) {
            contextDto.setSuccess(false);
            contextDto.setMessage("用户已被锁定");
            return null;
        }

        sysLoginDto.setSysUserDto(user);
        sysLoginDto.setToken(this.calcToken(user));
        sysUserService.loadPermissionsAndRoles(user);

        if (sysLoginDto.getRememberMe()) {
            sysLoginDto.setExpire(30); // token 30天过期
            redisService.set(ERedisDefine.TOKEN_PREFIX.getCode() + sysLoginDto.getToken(), JSONUtils.dtoToString(user),
                    Expiration.from(30, TimeUnit.DAYS), null);
        }

        return sysLoginDto;
    }

    public boolean accountProtect(ContextDto contextDto) {
        SysUserDto sysUserDto = JSONUtils.getRequestData(contextDto, SysUserDto.class);

        String falseTimes = redisService.get(USER_LOGIN_FAIL_TIMES.getCode() + sysUserDto.getUsername());
        if (falseTimes != null && Integer.parseInt(falseTimes) >= 3) {
            contextDto.setSuccess(false);
            contextDto.setMessage("连续输错密码3次，账号锁定5分钟");
            return false;
        }

        return true;
    }

    /**
     * 删除用户登陆失败的缓存
     * @param sysUserDto
     * @return
     */
    public void clearLoginFailCache(SysUserDto sysUserDto) {
        Long count = redisService.delete(USER_LOGIN_FAIL_TIMES.getCode() + sysUserDto.getUsername());
        log.info("登陆成功.删除用户登陆失败缓存数据 {}  数量 {}", USER_LOGIN_FAIL_TIMES.getCode() + sysUserDto.getUsername(), count);
    }

    public Long incrTimesForAccoutLoginError(ContextDto contextDto) {
        SysUserDto sysUserDto = JSONUtils.getRequestData(contextDto, SysUserDto.class);

        Long n = redisService.incr(USER_LOGIN_FAIL_TIMES.getCode() + sysUserDto.getUsername());
        redisService.expire(USER_LOGIN_FAIL_TIMES.getCode() + sysUserDto.getUsername(), TimeUnit.MINUTES.toSeconds(5));
        return n;
    }

    private SysUserDto findUserByUsernameAndPassword(String username, String password) {
        SysUserExample example = new SysUserExample();
        example.createCriteria().andPasswordEqualTo(password).andUsernameEqualTo(username);
        List<SysUserDto> sysUserDtos = sysUserMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(sysUserDtos)) {
            return null;
        }
        return sysUserDtos.get(0);
    }

    /**
     * 计算token
     *
     * @param sysUserDto
     */
    private String calcToken(SysUserDto sysUserDto) {
        String username = sysUserDto.getUsername();
        String now = DateUtils.formatDate(new Date(), "yyyy-MM-dd HH:mm:ss");
        String token = new String(Base64Utils.encode((username + "|" + now).getBytes()));
        return token;
    }

    /**
     * 登出
     *
     * @param contextDto
     * @return
     */
    public SysLoginDto logout(ContextDto contextDto) {
        SysLoginDto sysLoginDto = JSONUtils.getRequestData(contextDto, SysLoginDto.class);
        Long delete = redisService.delete(ERedisDefine.TOKEN_PREFIX + sysLoginDto.getToken());
        if (delete == null || delete == 0) {
            log.error("用户登出删除token失败 token:{} user:{}", sysLoginDto.getToken(), sysLoginDto.getUser());
        }
        return null;
    }

}
